//
// echo_server.cpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/asio.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/write.hpp>
#include <iostream>
#include <memory>
#include <signal.h>

using boost::asio::ip::tcp;

class session : public std::enable_shared_from_this<session> {
public:
  explicit session(boost::asio::io_context &io_context, tcp::socket socket)
      : socket_(std::move(socket)), timer_(io_context),
        strand_(io_context.get_executor()) {
    int one = 1;
    setsockopt(socket_.native_handle(), IPPROTO_TCP, TCP_NODELAY,
               reinterpret_cast<char *>(&one), sizeof(one));
    int buffer_size = 65536; // 设定为64KB
    setsockopt(socket_.native_handle(), SOL_SOCKET, SO_SNDBUF,
               (char *)&buffer_size, sizeof(buffer_size));
    setsockopt(socket_.native_handle(), SOL_SOCKET, SO_RCVBUF,
               (char *)&buffer_size, sizeof(buffer_size));
  }

  void go() {
    auto self(shared_from_this());
    boost::asio::spawn(
        strand_,
        [this, self](boost::asio::yield_context yield) {
          try {
            for (;;) {
              timer_.expires_after(std::chrono::seconds(10));
              char data[128];
              memset(data, 0, 128 * sizeof(char));
              std::size_t n = 0;
              do {
                n = socket_.async_read_some(boost::asio::buffer(data), yield);
              } while (0 == strlen(data));
              std::cout << "############## out read loop success: data: "
                        << data << std::endl;
              boost::asio::async_write(socket_, boost::asio::buffer(data, n),
                                       yield);
              std::cout << "######### write success" << std::endl;
            }
          } catch (std::exception &e) {
            socket_.close();
            timer_.cancel();
          }
        },
        boost::asio::detached);

    boost::asio::spawn(
        strand_,
        [this, self](boost::asio::yield_context yield) {
          while (socket_.is_open()) {
            boost::system::error_code ignored_ec;
            timer_.async_wait(yield[ignored_ec]);
            if (timer_.expiry() <=
                boost::asio::steady_timer::clock_type::now()) {
              socket_.close();
            }
          }
        },
        boost::asio::detached);
  }

private:
  tcp::socket socket_;
  boost::asio::steady_timer timer_;
  boost::asio::strand<boost::asio::io_context::executor_type> strand_;
};

int main(int argc, char *argv[]) {
  try {
    if (argc != 2) {
      std::cerr << "Usage: echo_server <port>\n";
      return 1;
    }
    boost::asio::io_context io_context;

    boost::asio::signal_set signals(io_context);
    signals.add(SIGINT);
    signals.add(SIGTERM);
    signals.async_wait(
        [&io_context](boost::system::error_code, int) { io_context.stop(); });
    boost::asio::spawn(
        io_context,
        [&](boost::asio::yield_context yield) {
          tcp::acceptor acceptor(io_context,
                                 tcp::endpoint(tcp::v4(), std::atoi(argv[1])));

          for (;;) {
            boost::system::error_code ec;
            tcp::socket socket(io_context);
            acceptor.async_accept(socket, yield[ec]);
            if (!ec) {
              std::make_shared<session>(io_context, std::move(socket))->go();
            }
          }
        },
        [](std::exception_ptr e) {
          if (e)
            std::rethrow_exception(e);
        });

    io_context.run();
  } catch (std::exception &e) {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}
